import 'cheerio';

import { Injectable } from '@nestjs/common';
import { ChatGetModalDto } from './dto/chat-getModal';
import { UseOpenAi } from 'utils/useOpenAi';
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from '@langchain/core/prompts';
import { Document } from '@langchain/core/documents';
import { createStuffDocumentsChain } from 'langchain/chains/combine_documents';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { CheerioWebBaseLoader } from '@langchain/community/document_loaders/web/cheerio';
import { OpenAIEmbeddings } from '@langchain/openai';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { createRetrievalChain } from 'langchain/chains/retrieval';
import { FRIDAY_LLM_CONFIG } from 'utils/constants';
import {
  StringOutputParser,
  CommaSeparatedListOutputParser,
} from '@langchain/core/output_parsers';
import { StructuredOutputParser } from 'langchain/output_parsers';
import { z } from 'zod';
import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages';
import { createHistoryAwareRetriever } from 'langchain/chains/history_aware_retriever';
import { agent } from 'utils/agent';
import { memory } from 'utils/memory';

@Injectable()
export class ChatService {
  async getModal(body: ChatGetModalDto) {
    // console.log(body, '>>>>>');a
    const b = body;
    // const b = {
    //   modal: 'gpt-4.1',
    //   content: '宋响林是是谁',
    // };
    const llm = await UseOpenAi.getAgent(b.modal);
    const response = await llm.invoke({
      messages: [
        // new HumanMessage('你好'),
        { role: 'system', content: '使用中文回答' },
        {
          role: 'system',
          content: `##response
          直接回答，不需要任何自述的文字,
          `,
        },
        //HumanMessage === role: 'user'
        {
          role: 'user',
          content: [{ type: 'text', text: '用户问题：' + b.content }],
        },
        // new HumanMessage(b.content),
      ],
    });

    return response.messages.at(-1)?.content;
  }
  async getChain(body: ChatGetModalDto) {
    const b = body;
    // const llm = await UseOpenAi.getAgent(b.modal);
    const modal = UseOpenAi.getLLM(b.modal);
    // 01- prompt-->fromTemplate
    // const prompt = ChatPromptTemplate.fromTemplate(`
    //   请根据{input},说一个300字的笑话
    //   `);
    //02-  prompt--> fromMessages
    const prompt = ChatPromptTemplate.fromMessages([
      ['ai', '根据用户的输入生成一段笑话'],
      ['human', '{input}'],
      // new HumanMessage(b.content),//
    ]);
    const chain = prompt.pipe(modal);
    const response = await chain.invoke({ input: b.content });
    return response;
    // llm
  }

  async stringOutputParser(body: ChatGetModalDto) {
    const b = body;
    const modal = UseOpenAi.getLLM(b.modal);
    const prompt = ChatPromptTemplate.fromMessages([
      ['ai', '根据用户的输入生成一段笑话'],
      ['human', '{input}'],
    ]);

    /**
    1- 字符串解析器，去自动取消换航符号"\n""
    const parse = new StringOutputParser();

      response：
      ```jsx
      这里是一个关于美团和淘宝闪购的小笑话：
      ---
      **外卖大战**
      小明同时打开了美团和淘宝闪购，想看看哪个更快。

      美团：30分钟内送达，超时赔付！
      淘宝闪购：1小时闪电送达，急速体验！

      结果30分钟后，美团外卖员到了：
      "先生，您的外卖到了！"

      1小时后，淘宝闪购也到了：
      "先生，您的闪购商品到了！"

      小明疑惑地问淘宝配送员："为什么叫闪购啊？"

      配送员淡定回答："因为您的钱包余额会'闪'没的很快啊！"

      小明看了看美团外卖员，又看了看淘宝配送员，突然明白了什么叫"左手外卖右手购物，钱包比闪电还要快"...

      ---

      😄 生活中确实经常遇到这种"双重诱惑"呢！
      ```

    
     * 默认： "content": "哈哈，来个关于美团和淘宝闪购的笑话：\n\n**美团 vs 淘宝闪购的终极对决**\n\n小明同时打开了美团和淘宝闪购，想买点零食。\n\n美团：30分钟送达，外卖小哥风雨无阻！\n淘宝闪购：1小时送达，品类丰富任你选！\n\n小明犹豫了半天，最后决定——\n\n在美团上点了个炸鸡，在淘宝闪购买了可乐。\n\n结果炸鸡30分钟到了，可乐1小时后才来...\n\n小明看着冷掉的炸鸡和刚到的冰可乐，陷入了沉思：\n\"我这是在考验自己的耐心，还是在锻炼肠胃的适应能力？\"\n\n最后小明悟出了人生真理：\n同步下单容易，同步收货靠缘分！ 😂\n\n---\n\n怎么样，这个笑话还合你胃口吗？生活中确实经常遇到这种\"时间差\"的小尴尬呢！",
    */
    const parse = new StringOutputParser();
    const chain = prompt.pipe(modal).pipe(parse);
    return await chain.invoke({ input: b.content });
  }
  async commaSeparatedListOutputParser(body: ChatGetModalDto) {
    const b = body;
    const modal = UseOpenAi.getLLM(b.modal);
    const prompt = ChatPromptTemplate.fromMessages([
      ['ai', '根据用户的输入生成5个单词'],
      ['human', '{input}'],
    ]);

    /**
    2- 列表解析器
    默认： "content": "基于\"happy\"这个词，我为你生成5个相关单词：\n\n1. **joyful** - 快乐的\n2. **cheerful** - 愉快的  \n3. **delighted** - 高兴的\n4. **content** - 满足的\n5. **elated** - 兴高采烈的\n\n这些词都表达了积极正面的情绪状态，与\"happy\"在语义上相近。",
    使用解析器： [
        "基于 \"happy\" 这个单词，我为您生成5个相关的英文单词：\n\n1. **joyful** - 快乐的，充满喜悦的\n2. **cheerful** - 愉快的，开朗的\n3. **delighted** - 高兴的，欣喜的\n4. **content** - 满足的，满意的\n5. **euphoric** - 欣快的，极度愉悦的\n\n这些单词都表达了积极正面的情感状态，与\"happy\"在语义上相关联。"
    ]
        Q： 好像不好用
    */
    const parse = new CommaSeparatedListOutputParser();
    const chain = prompt.pipe(modal).pipe(parse);
    return await chain.invoke({ input: b.content });
  }
  async structuredOutputParser(body: ChatGetModalDto) {
    const b = body;
    const modal = UseOpenAi.getLLM(b.modal);
    const prompt = ChatPromptTemplate.fromTemplate(`
      根据信息提取数据，
      fmt:{fmt},      
      input：{input}
      `);
    //JSON 示例
    // const parse = StructuredOutputParser.fromNamesAndDescriptions({
    //   name: 'name of the person',
    //   age: 'age of the person',
    //   height: 'height of the person',
    //   weight: 'weight of the person',
    // });

    //zod 示例
    const parse = StructuredOutputParser.fromZodSchema(
      z.object({
        name: z.string().describe('name of the person'),
        age: z.number().describe('age of the person'),
        height: z.number().describe('height of the person'),
        weight: z.number().describe('weight of the person'),
        gender: z.string().optional().describe('gender of the person'),
      }),
    );
    console.log(123, parse.getFormatInstructions());
    const chain = prompt.pipe(modal).pipe(parse);
    return await chain.invoke({
      input: b.content,
      fmt: parse.getFormatInstructions(), //给出格式
    });
  }
  async getParse(body: ChatGetModalDto) {
    //1- 字符串解析器，去自动取消换航符号"\n""
    // const response = this.stringOutputParser(body);
    //2- 列表解析器
    // const response = this.commaSeparatedListOutputParser(body);
    //3-结构化解析器
    /**
    {
          "content":"张三今年18岁，身高1米8，体重60kg"
      }
     */
    const response = this.structuredOutputParser(body);

    return response;
    // llm
  }

  async test01(body: ChatGetModalDto) {
    const b = body;
    //  获取模型
    const modal = UseOpenAi.getLLM(b.modal);

    const prompt = ChatPromptTemplate.fromTemplate(`
      回答用户的问题,
      context: duo-cli是到店交易标准化项目给RD提供的一套指令集，集成了源码物料发布到DUO资产平台、打包utils的能力。
      input: {input}
      `);

    const chain = prompt.pipe(modal);

    const response = await chain.invoke({ input: b.content });
    return response;
  }
  async test02(body: ChatGetModalDto) {
    const b = body;
    //  获取模型
    const modal = UseOpenAi.getLLM(b.modal);

    const prompt = ChatPromptTemplate.fromTemplate(`
      回答用户的问题,
      context: {context}
      input: {input}
      `);

    // const chain = prompt.pipe(modal);
    const docsA = new Document({
      pageContent:
        'duo-cli是到店交易标准化项目给RD提供的一套指令集，集成了源码物料发布到DUO资产平台、打包utils的能力。',
    });
    // 创建文档链****** 新
    const chain = await createStuffDocumentsChain({
      llm: modal,
      prompt,
    });

    const response = await chain.invoke({ input: b.content, context: [docsA] });
    return response;
  }
  async test03(body: ChatGetModalDto) {
    const b = body;
    //  获取模型
    const modal = UseOpenAi.getLLM(b.modal);

    const prompt = ChatPromptTemplate.fromTemplate(`
      回答用户的问题,
      context: {context}
      input: {input}
      `);

    // const chain = prompt.pipe(modal);
    // const docsA = new Document({
    //   pageContent:
    //     'duo-cli是到店交易标准化项目给RD提供的一套指令集，集成了源码物料发布到DUO资产平台、打包utils的能力。',
    // });

    // 创建文档链****** 新
    const chain = await createStuffDocumentsChain({
      llm: modal,
      prompt,
    });

    const loader = new CheerioWebBaseLoader(
      b.url || 'https://baike.baidu.com/item/NPM/23807941',
      // 'https://km.sankuai.com/collabpage/2128353494', // SSO限制，无法直接访问 需要加cookie

      // {
      //   // selector: pTagSelector,
      //   headers: {
      //     Cookie:
      //       '_lxsdk_cuid=1970b84b199c8-0bca89aef6d6b58-19525636-1fa400-1970b84b199c8; _lxsdk=1970b84b199c8-0bca89aef6d6b58-19525636-1fa400-1970b84b199c8; s_u_745896=qm/a1BQcjM5+bVdjJ4owEg==; ct_misid=wb_wangkang11; uu=97a08630-41d3-11f0-8a37-e5a46050f873; csrf=7Az4QBSnEsTmdzsXtCyGk28x3XN3trdx; e_b_id_352126=f5db9945c9b868a16c37b38f8dfa4b0c; e_u_id_3299326472=5bff1e8d1b074da86f443836e3caa891; u=3449370880; WEBDFPID=5151uv123u1v55841vu55u9012825w6x8029y15y053579586v19y161-1755759702208-1748244936129GIQYSOK75613c134b6a252faa6802015be905512298; utm_source_rg=AM%25e3I.K.A%25455; moa_deviceId=665C81A097D45610B69E98ADC298D2D1; s_m_id_3299326472=AwMAAAA5AgAAAAIAAAE9AAAALKLM+OSh1jcysJsfBZ1trvowcr21iXKXvZsg7hgbR5gdEkF+lPabmHOEVftVAAAAJEWcyhWqEnvgecWeFLjg66zhH0eySLZRW71hXcwQxQj4MCJpuQ==; com.sankuai.it.ead.citadel_ssoid=eAGFzjtKA0EAgGEGm4AIYmk1paRY5v2wcrMriiIqKhIbmaesiZsQI8EbGCwMwSqVCJbxCKIXsBQ8h3ZBojmBxd_-fBWw-PX-Mg_7T2-TASXLrnWRXJqycWWKpOgmwfjEFV3jQ3MVBoWQjtp6ZQgTVipMSDUSZJmTUnBZG4AlKATPFE6RljnjAqOa0OtapXlGtMpJjuHz7efrPV0B5N-fmsnW5jbHk-_pHd0bf0xHN3QIKsfBHrhQhkcAXeRcRuGwjMRJzSKLjlBEMMJOmIBOIPXIeaxYpFXOvDbGeos5Edio4DUNQwBrO3S_Hs1Wu9M42s3P6612u7ORNt01F1mxfehHYKFnT3umPGv8hfED6P_MRL_azmZg**eAEFwQkBwDAIA0BLwEjXyOH1L6F35JDR7SqhM4Ao6GI_Su2Y67Qt1xeWNf1JVqAXeoqW9z4uQhGF**bvPVQbuapWk_my2N57scaAnAuSDwlOhoFaEZrBfLUYgt3MDwW9mpI9r2ldvkgadz62AihtSYqJZcYdiNGM8_AQ**MjQwOTQ1Nzksd2Jfd2FuZ2thbmcxMSznjovlurcsd2Jfd2FuZ2thbmcxMUBtZWl0dWFuLmNvbSwxLGVkY18yNDA5NDU3OSwxNzU2OTAwNjQzNDY1; ct_token=eAGFzjtKA0EAgGEGm4AIYmk1paRY5v2wcrMriiIqKhIbmaesiZsQI8EbGCwMwSqVCJbxCKIXsBQ8h3ZBojmBxd_-fBWw-PX-Mg_7T2-TASXLrnWRXJqycWWKpOgmwfjEFV3jQ3MVBoWQjtp6ZQgTVipMSDUSZJmTUnBZG4AlKATPFE6RljnjAqOa0OtapXlGtMpJjuHz7efrPV0B5N-fmsnW5jbHk-_pHd0bf0xHN3QIKsfBHrhQhkcAXeRcRuGwjMRJzSKLjlBEMMJOmIBOIPXIeaxYpFXOvDbGeos5Edio4DUNQwBrO3S_Hs1Wu9M42s3P6612u7ORNt01F1mxfehHYKFnT3umPGv8hfED6P_MRL_azmZg**eAEFwQkBwDAIA0BLwEjXyOH1L6F35JDR7SqhM4Ao6GI_Su2Y67Qt1xeWNf1JVqAXeoqW9z4uQhGF**bvPVQbuapWk_my2N57scaAnAuSDwlOhoFaEZrBfLUYgt3MDwW9mpI9r2ldvkgadz62AihtSYqJZcYdiNGM8_AQ**MjQwOTQ1Nzksd2Jfd2FuZ2thbmcxMSznjovlurcsd2Jfd2FuZ2thbmcxMUBtZWl0dWFuLmNvbSwxLGVkY18yNDA5NDU3OSwxNzU2OTAwNjQzNDY1; logan_session_token=qiud8kpa0hq5fdehbyz7; _lxsdk_s=198fffd4452-f29-35b-356%7C%7C619',
      //   },
      // },
    );
    const docs = await loader.load();

    const response = await chain.invoke({ input: b.content, context: docs });
    return response;
  }
  async test04(body: ChatGetModalDto) {
    const b = body;
    //  获取模型
    const modal = UseOpenAi.getLLM(b.modal);

    const prompt = ChatPromptTemplate.fromTemplate(`
      回答用户的问题,
      context: {context}
      input: {input}
      `);

    const chain = await createStuffDocumentsChain({
      llm: modal,
      prompt,
    });

    const loader = new CheerioWebBaseLoader(
      b.url!,
      // 'https://baike.baidu.com/item/NPM/23807941',
    );
    const docs = await loader.load();
    //文档切片
    const splitter = new RecursiveCharacterTextSplitter({
      chunkSize: 200,
      chunkOverlap: 20,
    });
    // 文档切片
    const splitterDocs = await splitter.splitDocuments(docs);
    //向量
    const embeddings = new OpenAIEmbeddings({
      verbose: true, // 控制台日志
      model: 'text-embedding-3-large',
      configuration: {
        baseURL: FRIDAY_LLM_CONFIG.API_BASE_URL,
        apiKey: FRIDAY_LLM_CONFIG.API_KEY,
      },
    });
    // 向量存储
    const vectorStore = await MemoryVectorStore.fromDocuments(
      splitterDocs,
      embeddings,
    );
    // 检索器
    const retriever = vectorStore.asRetriever({ k: 4 }); // k-每次检索返回的文档片段
    //新的检索链
    const retrievalChain = await createRetrievalChain({
      retriever,
      combineDocsChain: chain, //检索链
    });

    const response = await retrievalChain.invoke({
      // prompt 中 上下文必须是 context: {context}
      // 用户输入必须是input
      input: b.content,
      //自动添加上下文
      // context: docs,
    });
    return response;
  }

  async createVectorStore() {
    const loader = new CheerioWebBaseLoader(
      // b.url!,
      'https://baike.baidu.com/item/NPM/23807941',
    );
    const docs = await loader.load();
    //文档切片
    const splitter = new RecursiveCharacterTextSplitter({
      chunkSize: 200,
      chunkOverlap: 20,
    });
    // 文档切片
    const splitterDocs = await splitter.splitDocuments(docs);
    //向量
    const embeddings = new OpenAIEmbeddings({
      verbose: true, // 控制台日志
      model: 'text-embedding-3-large',
      configuration: {
        baseURL: FRIDAY_LLM_CONFIG.API_BASE_URL,
        apiKey: FRIDAY_LLM_CONFIG.API_KEY,
      },
    });
    // 向量存储
    const vectorStore = await MemoryVectorStore.fromDocuments(
      splitterDocs,
      embeddings,
    );
    return vectorStore;
  }
  async createChain(vectorStore: MemoryVectorStore) {
    // const b = body;
    const modal = UseOpenAi.getLLM('gpt-4.1');

    // const prompt = ChatPromptTemplate.fromTemplate(`
    //   回答用户的问题,
    //   context: {context},
    //   Chat history: {chat_history}
    //   input: {input}
    //   `);
    const prompt = ChatPromptTemplate.fromMessages([
      ['system', '根据上下文回答用户问题：{context}'],
      new MessagesPlaceholder('chat_history'), //添加占位符
      ['user', '{input}'],
    ]);

    const chain = await createStuffDocumentsChain({
      llm: modal,
      prompt,
    });
    // 检索器
    const retriever = vectorStore.asRetriever({ k: 4 }); // k-每次检索返回的文档片段
    // 创建提示词模版
    const retrieverPrompt = ChatPromptTemplate.fromMessages([
      new MessagesPlaceholder('chat_history'), //添加占位符
      ['user', '{input}'],
      ['user', '查找相关的内容'],
    ]);
    // 创建一个历史aware的检索器
    const historyAwareRetriever = await createHistoryAwareRetriever({
      llm: modal,
      retriever,
      rephrasePrompt: retrieverPrompt,
    });
    //新的检索链
    const retrievalChain = await createRetrievalChain({
      combineDocsChain: chain, //检索链
      retriever: historyAwareRetriever,
    });

    return retrievalChain;
  }
  async test05(body: ChatGetModalDto) {
    const b = body ?? { content: '我是谁' };
    // 创建向量存储
    const vectorStore = await this.createVectorStore();
    // 创建检索链
    const chain = await this.createChain(vectorStore);

    const chatHistory: BaseMessage[] = [
      new AIMessage('你好，有什么可以帮助你'),
      new HumanMessage('你好，我是张三'),
      new AIMessage('你好，张三'),
      new AIMessage('今天天气怎么样'),
    ];

    const response = await chain.invoke({
      input: b.content,
      chat_history: chatHistory,
    });
    return response;
  }
  async getDocument(body: ChatGetModalDto) {
    //https://www.bilibili.com/video/BV1xb3wzhEDU?vd_source=7ef7713ee296fe3613d4d2e4627f05bb&spm_id_from=333.788.player.switch&p=4
    //1- 使用硬编码
    // const response = this.test01(body);
    //2- 使用指定文本链
    // const response = this.test02(body);
    // 3-  使用cheerio
    //  "content":"这个文章说的什么"
    // const response = this.test03(body);
    // 4-分割文档
    // const response = this.test04(body);
    //5- 封装函数
    const response = this.test05(body);

    return response;
  }
  async getAgent() {
    return await agent();
  }
  async getMemory() {
    return await memory();
  }
}
